C#设计模式—— 单例模式(简单的说)
单例模式:确保一个类只有一个实例,并提供一个全局访问点。(定义)
概念拆解:
(1)确保一个类只有一个实例
(2)提供一个访问它的全局访问点
个人理解:
一个类不被new,在类里的方法不被重复的new,在多线程调用实例时,确保只有一个实例在运行。
生活中的例子:
一个国家只有一个总统。
简单的单例模式代码:
/// <summary> /// 单例模式的实现 /// </summary> public class Singleton { // 定义一个静态变量来保存类的实例 private static Singleton uniqueInstance; // 定义私有构造函数,使外界不能创建该类实例 private Singleton() { } /// <summary> /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
》》在多线程中,需要确保一个实例。(我们可以使用线程锁lock来控制 )
/// <summary> /// 单例模式的实现 /// </summary> public class Singleton { // 定义一个静态变量来保存类的实例 private static Singleton uniqueInstance; // 定义一个标识确保线程同步 private static readonly object locker = new object(); // 定义私有构造函数,使外界不能创建该类实例 private Singleton() { } /// <summary> /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 当第一个线程运行到这里时,此时会对locker对象 "加锁", // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁 // lock语句运行完之后(即线程运行完之后)会对该对象"解锁" lock (locker) { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } return uniqueInstance; } }
》》多线程的“双重锁定”(目的:为了减少不必要的开销)
/// <summary> /// 单例模式的实现 /// </summary> public class Singleton { // 定义一个静态变量来保存类的实例 private static Singleton uniqueInstance; // 定义一个标识确保线程同步 private static readonly object locker = new object(); // 定义私有构造函数,使外界不能创建该类实例 private Singleton() { } /// <summary> /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 当第一个线程运行到这里时,此时会对locker对象 "加锁", // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁 // lock语句运行完之后(即线程运行完之后)会对该对象"解锁" // 双重锁定只需要一句判断就可以了 if (uniqueInstance == null) { lock (locker) { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
简单模拟网站计数功能
public class Singleton { private static Singleton instance;//静态实例 private static readonly object locker = new object();//静态锁 public int count = 1; private Singleton() {//构造函数 while (true) { Console.ReadKey(); count += 1; Console.WriteLine(count.ToString()); } } public static Singleton GetInstance()//方法,方法中去实例化类. { if (instance == null) { lock(locker) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
在某个时候我只需要一个线程去处理事务,不想有多个实例时。例如我们建立一个数据库存取的管理类。类中有许多的方法。
public class DBManager { private static DBManager uniqueInstance;//定义一个静态变量来保存类的实例 private static readonly object locker = new object();//定义一个线程锁 private static SQLiteHelp sqliteHelper;//实例化一个数据库连接 #region 全局访问点 private DBManager() { string dbPath = Application.StartupPath + "\\Data.thl"; sqliteHelper = new SQLiteHelp("data source=" + dbPath + ";Pooling=true;FailIfMissing=false"); } /// <summary> /// 全局访问点 /// </summary> /// <returns></returns> public static DBManager GetInstance() { if (uniqueInstance == null) { lock (locker) { if (uniqueInstance == null) { uniqueInstance = new DBManager(); } } } return uniqueInstance; } #endregion #region 文件列表操作 /// <summary> /// 插入路径 /// </summary> /// <param name="path">路径</param> /// <returns></returns> public bool InsertFilePath(string path) { string sql = "insert into RunInfoList (FilePath,Status) values ('" + path + "','停止')"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 插入路径 /// </summary> /// <param name="serverId">游服ID</param> /// <param name="path">路径</param> /// <returns></returns> public bool InsertFilePath(int serverId,string path) { string sql = "insert into RunInfoList (ServerId,FilePath,Status) values ('"+serverId+"','" + path + "','停止')"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 删除 /// </summary> /// <param name="path">路径</param> /// <returns></returns> public bool DeleteFilePath(string path) { string sql = "delete from RunInfoList where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 更新最后的备份时间 /// </summary> /// <param name="path">路径</param> /// <returns></returns> public bool UpdateTime(string path) { string sql = "update RunInfoList set LastBackupTime=datetime('now') where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 获取文件列表 /// </summary> /// <returns></returns> public List<FileListInfo> GetFileList() { string sql = "select * from RunInfoList"; var reader = sqliteHelper.ReturnDataReader(sql); List<FileListInfo> fileList = new List<FileListInfo>(); while (reader.Read()) { FileListInfo data = new FileListInfo(); if (!Convert.IsDBNull(reader["ServerId"])) { data.ServerId = Convert.ToInt32(reader["ServerId"]); } if (!Convert.IsDBNull(reader["FilePath"])) { data.FilePath = reader["FilePath"].ToString(); } if (!Convert.IsDBNull(reader["Status"])) { data.Status = reader["Status"].ToString(); } if (!Convert.IsDBNull(reader["LastBackupTime"])) { data.LastBackTime = reader["LastBackupTime"].ToString(); } fileList.Add(data); } return fileList; } /// <summary> /// 更新ServerId /// </summary> /// <param name="path"></param> /// <returns></returns> public bool UpdateServerId(string path,string serverid) { string sql = "update RunInfoList set ServerId='"+serverid+"' where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } #endregion }
说了单例模式的一些概念和代码,重要的是我们要怎么应用在实际的开发中?以下是我在博客园找到的关于单例的运用场景。
1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
9. HttpApplication 也是单位例的典型应用。熟悉ASP.NET(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
总结以上,不难看出:
单例模式应用的场景一般发现在以下条件下:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
参考资料:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html